package com.jinhua.huaojcodesandbox;

import cn.hutool.core.io.FileUtil;
import com.jinhua.huaojcodesandbox.exception.BusinessException;
import com.jinhua.huaojcodesandbox.model.ExecuteCodeRequest;
import com.jinhua.huaojcodesandbox.model.ExecuteCodeResponse;
import com.jinhua.huaojcodesandbox.model.ExecuteMessage;
import com.jinhua.huaojcodesandbox.model.JudgeInfo;
import com.jinhua.huaojcodesandbox.model.enums.QuestionSubmitJudgeInfoMessageEnum;
import com.jinhua.huaojcodesandbox.model.enums.QuestionSubmitStateEnum;
import com.jinhua.huaojcodesandbox.utils.ProcessUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

public abstract class JavaNativeCodeSandBoxTemplate implements CodeSandBox {

    private static final String GLOBAL_CODE_DIR_NAME = "tmpCode";
    private static final String CODE_CLASS_NAME = "Main.java";
    private static final long TIME_OUT = 5000;
    private static final String SECURITY_PATH = "/home/huaoj/code/huaoj/src/main/resources/security";
    private static final String SECURITY_CLASS_NAME = "DefaultSecurityManager";
    private static final Logger log = LoggerFactory.getLogger(JavaNativeCodeSandBoxTemplate.class);

    /**
     * 保存代码文件
     *
     * @param code 需要保存的代码
     * @return 保存的代码文件
     */
    public File saveCodeFile(String code) {
        // 2. 创建代码执行文件
        //获取当前系统的根目录
        String userDir = System.getProperty("user.dir");

        String globalCodePathName = userDir + File.separator + GLOBAL_CODE_DIR_NAME;
        if (!FileUtil.exist(globalCodePathName)) {
            FileUtil.mkdir(globalCodePathName);
        }
        String userCodeParentPath = globalCodePathName + File.separator + UUID.randomUUID();
        String userCodePath = userCodeParentPath + File.separator + CODE_CLASS_NAME;
        File file = FileUtil.writeString(code, userCodePath, StandardCharsets.UTF_8);
        if (file == null){
            log.error("创建代码文件失败");
            throw new RuntimeException("创建代码文件失败");
        }
        return file;
    }

    public ExecuteMessage compileCodeFile(File codeFile) {
        try {
            String compileCMD = String.format("javac -encoding utf8 %s", codeFile.getAbsolutePath());
            Process compilerProcess = Runtime.getRuntime().exec(compileCMD);
            ExecuteMessage compileExecuteMessage = ProcessUtils.getProcessRunMessage(compilerProcess, " 编译");
            if (compileExecuteMessage.getExitValue() != 0) {
                throw new BusinessException(QuestionSubmitJudgeInfoMessageEnum.COMPILE_ERROR.getValue());
            }
            return compileExecuteMessage;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public List<ExecuteMessage> runFile(File codeFile, List<String> inputList) {
        List<ExecuteMessage> executeMessageList = new ArrayList<>();
        for (String input : inputList) {
//            cp 地址结束符windows为; linux为:
//            windows执行命令
//            String runCMD = String.format("java -Xmx1024m -Dfile.encoding=GBK -cp \"%s:%s\"  -Djava.security.manager=%s Main", codeFile.getParentFile().getAbsolutePath(),SECURITY_PATH, SECURITY_CLASS_NAME);
//            linux执行命令
            String runCMD = String.format("java -Xmx1024m -Dfile.encoding=GBK -cp %s:%s  -Djava.security.manager=%s Main", codeFile.getParentFile().getAbsolutePath(), SECURITY_PATH, SECURITY_CLASS_NAME);
            Process executeProcess;
            try {
                executeProcess = Runtime.getRuntime().exec(runCMD);
                ExecuteMessage executeMessage = ProcessUtils.runInteractProcess(executeProcess, input,"运行");
                executeMessageList.add(executeMessage);
//                超时控制  todo 程序十秒才能结束
//                new Thread(() -> {
//                    try {
//                        Thread.sleep(TIME_OUT);
//                        if (executeProcess != null) {
//                            System.out.println("超时");
//                            executeProcess.destroy();
//                            System.out.println(executeProcess);
//                        }
//                    } catch (InterruptedException e) {
//                        throw new RuntimeException(e);
//                    }
//                }).start();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return executeMessageList;
    }

    public ExecuteCodeResponse getOutputResponse(List<ExecuteMessage> executeMessageList) {
        ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse();
        JudgeInfo judgeInfo = new JudgeInfo();
        judgeInfo.setMessage(QuestionSubmitJudgeInfoMessageEnum.ACCEPTED.getValue());
        final long[] maxTime = {0l};
        final long[] maxMemory = {0l};

        List<String> outputList = executeMessageList.stream().map(item -> {
            maxTime[0] = Math.max(maxTime[0], item.getTime());
            maxMemory[0] = Math.max(maxMemory[0], item.getMemory());
            if (item.getExitValue() == 0) {
                return item.getMessage();
            } else {
                judgeInfo.setMessage(QuestionSubmitJudgeInfoMessageEnum.WRONG_ANSWER.getValue());
                return item.getErrorMessage();
            }
        }).collect(Collectors.toList());

        judgeInfo.setTime(maxTime[0]);
        judgeInfo.setMemory(maxMemory[0]);

        executeCodeResponse.setState(QuestionSubmitStateEnum.ACCOMPLISH.getValue());
        executeCodeResponse.setOutputList(outputList);
        executeCodeResponse.setExcuteInfo(judgeInfo);
        return executeCodeResponse;
    }

    public boolean deleteFile(File codeFile){
        if (codeFile != null){
            boolean del = FileUtil.del(codeFile.getParentFile().getAbsolutePath());
            log.info(del ? "删除成功" : "删除失败");
            return del;
        }
        return true;
    }


    @Override
    public ExecuteCodeResponse executeCode(ExecuteCodeRequest executeCodeRequest) {
        // 1.获取executeCodeRequest中的数据
        String code = executeCodeRequest.getCode();
        List<String> inputList = executeCodeRequest.getInputList();
        String language = executeCodeRequest.getLanguage();

        File codeFile = saveCodeFile(code);
        compileCodeFile(codeFile);
        List<ExecuteMessage> runExecuteMessage = runFile(codeFile, inputList);
        ExecuteCodeResponse executeCodeResponse = getOutputResponse(runExecuteMessage);
        boolean b = deleteFile(codeFile);
        return executeCodeResponse;
    }
}
